package tk.winshu.shortestpath.model;

import java.awt.*;
import java.awt.geom.AffineTransform;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

/**
 * @author Jason Krebs
 * @date 2015年2月2日
 */
public class Node {

    private static final int DEFAULT_SIZE = 50;

    private int index;
    private Image image;
    private Rectangle2D shape;

    /**
     * 终点指向当前节点的节点集
     * <p>
     * startNodes -> current
     */
    private List<Node> startNodes;
    /**
     * 起点为当前节点的节点集
     * <p>
     * current -> endNodes
     */
    private List<Node> endNodes;

    /**
     * 默认构造方法，导入时需要
     */
    public Node() {
        this(0, 0, 0);
    }

    public Node(int index, double x, double y) {
        this(index, x, y, null);
    }

    public Node(int index, Point2D point, Image image) {
        this(index, point.getX(), point.getY(), image);
    }

    private Node(int index, double x, double y, Image image) {
        this.index = index;
        this.startNodes = new ArrayList<>();
        this.endNodes = new ArrayList<>();
        this.setShape(x, y, image);
    }

    /**
     * 添加起点
     */
    public boolean addStartNode(Node startNode) {
        if (notContainsNode(startNode)) {
            return startNodes.add(startNode);
        }
        return false;
    }

    /**
     * 添加终点
     */
    public boolean addEndNode(Node node) {
        if (notContainsNode(node)) {
            return endNodes.add(node);
        }
        return false;
    }

    /**
     * 计算到另一节点的距离
     */
    public double distanceTo(Node other) {
        if (notContainsNode(other)) {
            return Integer.MAX_VALUE;
        }
        return Point2D.distance(getLocation().getX(), getLocation().getY(), other.getLocation().getX(), other.getLocation().getY());
    }

    /**
     * 清除与其他节点的关系
     */
    public void cleanRelations() {
        startNodes.clear();
        endNodes.clear();
    }

    /**
     * 判断是否与另一节点无连线关系
     */
    private boolean notContainsNode(Node node) {
        return !startNodes.contains(node) && !endNodes.contains(node);
    }

    /**
     * 绘制节点
     */
    public void draw(Graphics2D g2d) {
        AffineTransform t = new AffineTransform();
        t.translate(getRealLocation().getX(), getRealLocation().getY());
        t.scale(1, 1);
        g2d.drawImage(getImage(), t, null);
        g2d.drawString(getIndex() + "", (float) getRealLocation().getX(), (float) getRealLocation().getY());
    }

    /**
     * 获取所有的连线节点
     */
    public List<Node> getAllRelations() {
        List<Node> allNodes = new ArrayList<>(startNodes);
        allNodes.addAll(endNodes);
        return allNodes;
    }

    public double getHeight() {
        return this.shape.getHeight();
    }

    /**
     * 获取图像
     */
    public Image getImage() {
        return image;
    }

    /**
     * 获取标识
     */
    public int getIndex() {
        return index;
    }

    /**
     * 获取所有起点，不可修改
     */
    public List<Node> getStartNodes() {
        return Collections.unmodifiableList(startNodes);
    }

    /**
     * 获取以中心点的坐标
     */
    public Point2D getLocation() {
        return new Point2D.Double(this.shape.getCenterX(), this.shape.getCenterY());
    }

    /**
     * 获取所有连线数量
     */
    public int getNodeCount() {
        return startNodes.size() + endNodes.size();
    }

    /**
     * 获取所有终点
     */
    public List<Node> getEndNodes() {
        return Collections.unmodifiableList(endNodes);
    }

    /**
     * 获取以左上角为中心的坐标
     */
    public Point2D getRealLocation() {
        return new Point2D.Double(this.shape.getX(), this.shape.getY());
    }

    public Rectangle2D getShape() {
        return shape;
    }

    public double getWidth() {
        return this.shape.getWidth();
    }

    /**
     * 判断某个坐标是否在当前节点上
     */
    public boolean isContainsPoint(Point2D point) {
        return this.shape.contains(point);
    }

    /**
     * 移除一个连线关系
     */
    public void removeNode(Node node) {
        startNodes.remove(node);
        endNodes.remove(node);
    }

    public void setImage(Image image) {
        setShape(getLocation().getX(), getLocation().getY(), image);
    }

    public void setIndex(int id) {
        this.index = id;
    }

    public void setLocation(double x, double y) {
        setShape(x, y, getImage());
    }

    public void setLocation(Point2D point) {
        setLocation(point.getX(), point.getY());
    }

    /**
     * maybe for fast.json
     */
    public void setLocation(Point2D.Double point) {
        setLocation(point.getX(), point.getY());
    }

    /**
     * 设置图形及坐标
     */
    private void setShape(double px, double py, Image image) {
        if (image != this.image) {
            this.image = image;
        }

        double width = this.image == null ? DEFAULT_SIZE : this.image.getWidth(null);
        double height = this.image == null ? DEFAULT_SIZE : this.image.getHeight(null);
        if (this.shape == null) {
            this.shape = new Rectangle2D.Double();
        }

        this.shape.setFrame(px - width / 2, py - height / 2, width, height);
    }

    @Override
    public String toString() {
        return String.format("Node<index=%d,point=(%.2f,%.2f)>", getIndex(), getLocation().getX(), getLocation().getY());
    }

    @Override
    public int hashCode() {
        return Integer.valueOf(index).hashCode();
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj) {
            return true;
        }
        if (obj == null) {
            return false;
        }
        if (!(obj instanceof Node)) {
            return false;
        }
        return ((Node) obj).index == index;
    }
}
